package com.derekandbritt.booST.endpoints;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import org.opensocial.models.Person;

import android.util.Log;

import com.derekandbritt.booST.data.Cache;
import com.derekandbritt.booST.data.Challenge;
import com.derekandbritt.booST.data.EnergyLevel;
import com.derekandbritt.booST.data.User;
import com.derekandbritt.booST.exceptions.ConnectionException;
import com.derekandbritt.booST.exceptions.DataFormatException;
import com.derekandbritt.booST.exceptions.GenericException;

public class booSTEndpoint extends JSONEndpoint {

	private static final String ENDPOINT_NAME = "booST";
	private static final String DEFAULT_HOST_NAME = "boost-server.appspot.com"; // 10.0.2.2:8888 for localhost
	private static final String DEFAULT_CONTEXT_ROOT = "JSON";
	
	private static final String LOG_TAG = "booSTEndpoint";
	
	private static Cache<User> userCache = new Cache<User>(300000); // 5 minute cache lifetime
	
	private OpenSocialEndpoint osEndpoint;
	
	public booSTEndpoint(OpenSocialEndpoint osEndpoint) {
		this.osEndpoint = osEndpoint;
	}
	
	public void clearCaches() {
	    userCache.clearCache();
	}
	
	public String getEndpointName() {
		return ENDPOINT_NAME;
	}
	
	public String getDefaultHostName() {
		return DEFAULT_HOST_NAME;
	}

	public String getDefaultContextRoot() {
		return DEFAULT_CONTEXT_ROOT;
	}
	
	protected void preGetRequest(HashMap<String, String> map) {
	    try {
            String userId = osEndpoint.getPersonId();
            map.put("requestor", userId);
        } catch (GenericException e) { /* acceptable behavior */ }
	}
	
	public List<User> getFriends() throws ConnectionException, DataFormatException {
	    
	    ArrayList<User> friends = new ArrayList<User>();
	    
	    for(Person osFriend : osEndpoint.fetchFriends()) {
	        String friendId = OpenSocialEndpoint.getPersonId(osFriend);
	        User friend = getUser(friendId);
	        if(friend != null)
	            friends.add(friend);
	    }
	    
	    return friends;
	}
	
   public void refreshUser() {
       
       Runnable runnable = new Runnable() {
            public void run() {
                try {
                    userCache.updateCache(osEndpoint.getPersonId(), null);
                    getUser();
                } catch(GenericException e) {
                    Log.e(LOG_TAG, "Unable to refresh booST User", e);
                }
            }
        };
        
        Thread refreshThread = new Thread(runnable, "booSTThread");
        refreshThread.start();
   }
	
	public User getUser() throws ConnectionException, DataFormatException {
		return getUser(osEndpoint.getPersonId(), User.CREATE_IF_NEEDED_FLAG | 
				   								 User.INCLUDE_CHALLENGE_FLAG |
				   								 User.INCLUDE_EMOTION_FLAG |
				   								 User.INCLUDE_ENERGY_FLAG);
	}
	
	public User getUser(String userId) throws ConnectionException, DataFormatException {
		return getUser(userId, User.INCLUDE_EMOTION_FLAG |
		                       User.INCLUDE_ENERGY_FLAG);
	}
	
	public User getUser(String userId, int flags) throws ConnectionException, DataFormatException {
		
        //if the cache has more flags set than the request then serve from cache
        User user = userCache.getItem(userId);
        
        // disregard specific flag for the cache hit test
        int tempFlags = flags & ~User.CREATE_IF_NEEDED_FLAG & ~User.IGNORE_CACHE_FLAG;
        
        // return the object from the cache if it meets the requirements
        if(user != null && ((user.flags & tempFlags) ^ tempFlags) == 0 && (User.IGNORE_CACHE_FLAG & flags) == 0)
            return user;

        // remove the ignore cache bit from the flags if it was set
        flags = flags & ~User.IGNORE_CACHE_FLAG;
	    
		HashMap<String, String> map = new HashMap<String, String>();
        map.put("userId", userId);
        map.put("flags", flags + "");
        
        String jsonResult = getData("User", map, true);
		
        if(jsonResult == null || jsonResult.equals(""))
        	return null;
        else {
        	User result = gson.fromJson(jsonResult, User.class);
        	//set the cache
         	userCache.updateCache(userId, result);
        	return result;
        }
	}
	
	public EnergyLevel getCurrentEnergy(String userId) throws ConnectionException, DataFormatException {		
		EnergyLevel[] result = getEnergy(userId, 1);
		if(result.length > 0)
			return result[0];
		else
			return null;
	}
	
	public EnergyLevel[] getEnergy(String userId, int count) throws ConnectionException, DataFormatException {
		
		HashMap<String, String> map = new HashMap<String, String>();
        map.put("userId", userId);
        map.put("count", count + "");
        
        String jsonResult = getData("EnergyLevel", map, true);
		
        if(jsonResult == null || jsonResult.equals(""))
        	return new EnergyLevel[0];
        else
        	return gson.fromJson(jsonResult, EnergyLevel[].class);  
	}
	
	public void setEnergy(EnergyLevel energyLevel) throws ConnectionException, DataFormatException {
		String json = gson.toJson(energyLevel);
		sendData("EnergyLevel", json);
	}
	
	public void updateEnergy(String personId, double delta) throws ConnectionException, DataFormatException {
		EnergyLevel myEnergy = new EnergyLevel();
		myEnergy.setUserId(personId);
		myEnergy.setTimestamp(new Date(System.currentTimeMillis()));
		myEnergy.setLevel(delta);
		setEnergy(myEnergy);
	}
	
	public Challenge getChallenge(Long challengeId) throws ConnectionException, DataFormatException {
		
		HashMap<String, String> map = new HashMap<String, String>();
        map.put("id", challengeId + "");
                
        String jsonResult = getData("Challenges", map, true);
        
        if(jsonResult == null || jsonResult.equals(""))
        	return null;
        else
        	return gson.fromJson(jsonResult, Challenge.class);
	}

    public Challenge[] getActiveChallenges() throws ConnectionException, DataFormatException {
        User user = getUser();
        if(user.activeChallenges != null)
            return user.activeChallenges.toArray(new Challenge[0]);
        return new Challenge[0];
    }
	
	public Challenge[] getActiveChallenges(String userId, int count) throws ConnectionException, DataFormatException {
		return getChallenges(userId, "active", count);
	}

   public Challenge[] getCompletedChallenges(int count) throws ConnectionException, DataFormatException {
        return getChallenges(getUser().userID, "completed", count);
    }
	
	public Challenge[] getCompletedChallenges(String userId, int count) throws ConnectionException, DataFormatException {
		return getChallenges(userId, "completed", count);
	}

    public Challenge[] getChallenges(int count) throws ConnectionException, DataFormatException {
        return getChallenges(getUser().userID, null, count);
    }
	
	public Challenge[] getChallenges(String userId, int count) throws ConnectionException, DataFormatException {
		return getChallenges(userId, null, count);
	}
		
	private Challenge[] getChallenges(String userId, String status, int count) throws ConnectionException, DataFormatException {
		
		HashMap<String, String> map = new HashMap<String, String>();
        map.put("userId", userId);
        map.put("count", count + "");
        
        if(status != null)
        	map.put("status", status);
        
        String jsonResult = getData("Challenges", map, true);
		
        if(jsonResult == null || jsonResult.equals(""))
        	return new Challenge[0];
        else
        	return gson.fromJson(jsonResult, Challenge[].class);
	}
	
	public void setChallenge(Challenge challenge) throws ConnectionException, DataFormatException {
		String json = gson.toJson(challenge);
		sendData("Challenges", json);
	}
	
	public void logout() throws ConnectionException, DataFormatException {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put("userId", osEndpoint.getPersonId());
        map.put("online", "false");
        sendData("User", map, "");
	}
}